﻿using System.Collections.Generic;
using System;
using System.Reflection;
using System.Collections;

namespace Medianamik.Core.Sugar
{
    //Source : http://www.codeproject.com/KB/recipes/TreeIterations.aspx
    public static class Recurser<TItem, TCollection> where TCollection : IEnumerable
    {
        private static MethodInfo _GetChilds;

        static Recurser()
        {
            // since Reflection is very slow, this static constructor will be
            // executed only once per generic shaping
            Type tpItm = typeof(TItem);
            Type tpColl = typeof(TCollection);
            const BindingFlags BindFlags = BindingFlags.Public | BindingFlags.Instance;
            foreach (MemberInfo mi in tpItm.GetMembers(BindFlags))
            {
                MethodInfo Proposed = null;
                switch (mi.MemberType)
                {
                    case MemberTypes.Property:
                        PropertyInfo p = (PropertyInfo)mi;
                        if (p.PropertyType == tpColl)
                        {
                            Proposed = p.GetGetMethod();
                            break;
                        }
                        continue;
                    case MemberTypes.Method:
                        MethodInfo m = (MethodInfo)mi;
                        if (m.ReturnType == tpColl)
                        {
                            Proposed = m;
                            break;
                        }
                        continue;
                    default:
                        continue;
                }
                if (Proposed.GetParameters().Length == 0)
                {
                    _GetChilds = Proposed;
                    return;
                }
            }
            throw new ArgumentException(string.Concat(
               "can't find a recursive member in ", tpItm.Name, "!"));
        }

        public static IEnumerable<TItem> TopDown(TCollection Coll)
        {
            foreach (TItem Itm in Coll)
            {
                yield return Itm;
                TCollection Coll2 = (TCollection)_GetChilds.Invoke(Itm, null);
                foreach (TItem Itm2 in TopDown(Coll2)) { yield return Itm2; }
            }
        }

        public static IEnumerable<TItem> BottomUp(TCollection Coll)
        {
            foreach (TItem Itm in Coll)
            {
                TCollection Coll2 = (TCollection)_GetChilds.Invoke(Itm, null);
                foreach (TItem Itm2 in BottomUp(Coll2)) { yield return Itm2; }
                yield return Itm;
            }
        }
    }
}